<?php
namespace App\Http\Middleware;

use Closure;
use App\Api\Exceptions\ApiCommonException;

class SignCheck
{

    /**
     * 请求参数sign
     *
     * @var string
     */
    const REQUEST_ARG_SIGN = 'sign';

    /**
     * 请求参数current_timestap
     *
     * @var string
     */
    const REQUEST_ARG_CURRENT_TIMESTAMP = 'timestamp';

    /**
     * 请求参数random
     *
     * @var string
     */
    const REQUEST_ARG_RANDOM = 'random';

    /**
     * 服务客户端时间差
     *
     * @var integer
     */
    const SERVER_CLIENT_TIME_GAP = 600;

    /**
     * 返回请求过滤器
     *
     * @param \Illuminate\Http\Request $request
     * @param \Closure $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $args = $request->input();
        // 是否为开发开启环境
        $isRealDebug = $this->isRealDebug($args);
        unset($args['debug']);
        if (!$isRealDebug) {
            // timestamp检查
            if (isset($args[self::REQUEST_ARG_CURRENT_TIMESTAMP])) {
                $timestamp = $args[self::REQUEST_ARG_CURRENT_TIMESTAMP];
            } elseif (isset($args['atimestamp'])) {
                $timestamp = $args['atimestamp'];
            } else {
                $timestamp = 0;
            }

            $this->checkClientRequestTime($timestamp);
            // random检查
            $random = isset($args[self::REQUEST_ARG_RANDOM]) ? $args[self::REQUEST_ARG_RANDOM] : '';
            $this->checkRandom($random);
            // sign验证
            $signVal = isset($args[self::REQUEST_ARG_SIGN]) ? $args[self::REQUEST_ARG_SIGN] : '';
            $this->checkClientRequestSign($args, $signVal);
        }
        return $next($request);
    }

    /**
     * 检查客户端请求时间
     *
     * @param int $clientCurrentTimestampVal
     * @throws InvalidArgumentException
     * @return boolean
     */
    protected function checkClientRequestTime(int $clientCurrentTimestampVal)
    {
        if ($clientCurrentTimestampVal == 0) {
            throw ApiCommonException::timestampIsEmpty();
        }
        $serverCurrentTimestampVal = time();
        // 请求过期
        if ($clientCurrentTimestampVal < ($serverCurrentTimestampVal - self::SERVER_CLIENT_TIME_GAP)) {
            throw ApiCommonException::timestampOvertime();
        }
        // 超期
        if ($clientCurrentTimestampVal > ($serverCurrentTimestampVal + self::SERVER_CLIENT_TIME_GAP)) {
            throw ApiCommonException::timestampOverdue();
        }
        return true;
    }

    /**
     * 检查random
     *
     * @param string $random
     * @return boolean
     */
    protected function checkRandom(string $random)
    {
        if(empty($random)){
            throw ApiCommonException::randomIsEmpty();
        }
        $len = strlen($random);
        if ($len < 8 || $len > 20) {
            throw ApiCommonException::randomLengthErr();
        }
        return true;
    }

    /**
     * 检查请求签名
     *
     * @param array $args
     * @param string $signVal
     * @throws InvalidArgumentException
     */
    protected function checkClientRequestSign(array $args, string $signVal)
    {
        if (empty($signVal)) {
            throw ApiCommonException::signIsEmpty();
        }
        unset($args[self::REQUEST_ARG_SIGN]);
        // 排序
        ksort($args);
        array_walk($args, function(&$v){
            if (is_array($v)) {
                $v = json_encode($v);
            } elseif ($v === null) {
                $v = '';
            } else {

            }
        });
        $key = env('KEY');
        // 拼接
        $signString = urldecode(http_build_query($args)).'&key='.$key;
        // 加密
        $formatSignVal = md5($signString);
        if ($formatSignVal !== $signVal) {
            throw ApiCommonException::signCheckFail();
        }
        return true;
    }

    /**
     * 是否为真的debug
     *
     * @param array $args
     * @return boolean
     */
    protected function isRealDebug($args)
    {
        $debugInEnv = env("APP_DEBUG");
        $debugInQuery = $args['debug'] ?? false;
        return $debugInEnv || $debugInQuery;
    }
}